﻿using Microsoft.Extensions.ObjectPool;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;

namespace zijian666.Data
{
    internal class DbParameterFormatProvider : IFormatProvider, ICustomFormatter, IPooledObject
    {
        private IDbCommand _cmd;
        private IDbParamPlaceholderProvider _nameable;
        private readonly StringBuilder _buffer = new StringBuilder();
        private readonly List<IDataParameter> _parameters = new List<IDataParameter>();

        public void SetCommand(IDbCommand command)
        {
            _cmd = command;
            _nameable = DbParamPlaceholderProvider.Get(_cmd.Connection);
        }

        public object GetFormat(Type formatType) => this;

        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            if (arg is IEnumerable e && !arg.IsAtom())
            {
                var ee = e.GetEnumerator();
                if (ee.MoveNext())
                {
                    var p = AddParameter(ee.Current, format);
                    if (!ee.MoveNext())
                    {
                        return p;
                    }
                    _buffer.Append(p);
                    do
                    {
                        _buffer.Append(',');
                        _buffer.Append(AddParameter(ee.Current, format));
                    } while (ee.MoveNext());
                    return _buffer.ToString();
                }
                return AddParameter(null, format);
            }

            return AddParameter(arg, format);
        }

        private string AddParameter(object arg, string format)
        {
            if (arg is IDataParameter parameter)
            {
                _parameters.Add(parameter);
                return _nameable.GetPlaceholder(parameter);
            }
            var p = _cmd.CreateParameter();
            p.Value = arg.GetDbObject();
            _parameters.Add(p);
            if (string.IsNullOrEmpty(p.ParameterName))
            {
                p.ParameterName = "p" + (_parameters.Count + _cmd.Parameters.Count);
            }
            return _nameable.GetPlaceholder(p);
        }

        void IPooledObject.OnRetrunning()
        {
            if (_cmd != null)
            {
                if (_cmd.Parameters is DbParameterCollection ps)
                {
                    ps.AddRange(_parameters.ToArray());
                }
                else
                {
                    foreach (var p in _parameters)
                    {
                        _cmd.Parameters.Add(p);
                    }
                }
                _cmd = null;
            }
            if (_buffer.Length > 0)
            {
                _buffer.Clear();
            }
            if (_parameters.Count > 0)
            {
                _parameters.Clear();
            }
            _nameable = null;
        }
    }
}
